﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.ComponentModel;
using TvdP;
using Microsoft.Practices.Unity;

namespace Adapters
{
    /// <summary>
    /// Some silly interface.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IProperty<T>
    { T Value { set; get; } }

    /// <summary>
    /// A silly class
    /// </summary>
    public class PropertyImp : IProperty<object>
    {
        #region IProperty<string> Members

        public object Value
        {
            set { }
            get { return null; }
        }

        #endregion
    }

    /// <summary>
    /// Yasc
    /// </summary>
    public class PropertyImp2 
    {
        #region IProperty<string> Members

        public string Value
        {
            get; set;
        }

        #endregion
    }

    /// <summary>
    /// Ayasc
    /// </summary>
    public class PropertyImp3 : IProperty<string>, INotifyPropertyChanged
    {
        #region IProperty<string> Members

        string _value;

        public string Value
        {
            get { return _value; }
            set
            {
                if (value != _value)
                {
                    _value = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Value"));
                }
            }
        }

        #endregion

        public event PropertyChangedEventHandler PropertyChanged;
    }

    /// <summary>
    /// simple behavior that writes a message to the console when the Value property of a given IProperty&lt;string&gt; changes.
    /// </summary>
    public class LPGBehavior
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="prefix">The prefix to use in the message. THis is going to be injected by the unity container.</param>
        public LPGBehavior([Dependency("prefix")]string prefix)
        { _Prefix = prefix; }

        string _Prefix;
        IProperty<string> _Prp;
        INotifyPropertyChanged _Npc;

        public void Attach(IProperty<string> prp, INotifyPropertyChanged npc)
        {
            _Prp = prp;
            _Npc = npc;

            npc.PropertyChanged += npc_PropertyChanged;
        }

        void npc_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Value")
                Console.WriteLine(_Prefix + " " + _Prp.Value);           
        }
    }


    public class ITitle
    {
        public ITitle(Func<string> titelGetter)
        { _TitleGetter = titelGetter; }

        Func<string> _TitleGetter;
        public string Title { get { return _TitleGetter(); } } 
    }

    public class IHeadline
    {
        public IHeadline(Func<string> titelGetter)
        { _TitleGetter = titelGetter; }

        Func<string> _TitleGetter;
        public string Headline { get { return _TitleGetter(); } } 
    }

    public class Erm
    {
        public IHeadline _Headline;
        public ITitle _Title;

        public string Write()
        { return _Headline.Headline + _Title.Title; }
    }

    public class Dub
    { }

    //definition of a book
    public interface IBook
    { string Isbn { get; } }

    //definition of packagable objects
    public interface IPackagable
    {
        double Width { get; }
        double Height { get; }
        double Depth { get; }
        double Weight { get; }
    }

    //definition of items that can be read on an e-reader
    public interface IReadable
    {
        byte[] GetFrontCover();
        byte[] GetRearCover();
        byte[] GetIcon();
        System.IO.Stream GetEpubContent();
    }

    //implementation of IBook
    public class Book : IBook
    {
        public Book(string isbn)
        { Isbn = isbn; }

        public string Isbn { get; private set; }
    }

    //Implementation of IPackagable that binds to an IBook
    //Probably gets the data from some store. This is not implemented.
    public class PackageInformation
    {
        public double Width;
        public double Height;
        public double Depth;
        public double Weight;
    }

    public interface IPackageInformationService
    {
        PackageInformation GetInformationFor(string ItemKey);
    }

    public class PackageInformationService : IPackageInformationService
    {
        public PackageInformation GetInformationFor(string ItemKey)
        {
            //Dummy implementation
            return new PackageInformation { Width = 10.0, Height = 14.0, Depth = 3.0, Weight = 234.33 };
        }
    }

    public class BookAsPackagable : IPackagable
    {
        IPackageInformationService _PackageInformationService;
        IBook _Book;

        public BookAsPackagable(IBook book, IPackageInformationService packageInformationService)
        {
            _Book = book;
            _PackageInformationService = packageInformationService;
        }

        PackageInformation BookPackageInformation() 
        { return _PackageInformationService.GetInformationFor( "Book:" + _Book.Isbn ); } 

        public double Width
        { get { return BookPackageInformation().Width; } }

        public double Height
        { get { return BookPackageInformation().Height; } }

        public double Depth
        { get { return BookPackageInformation().Depth; } }

        public double Weight
        { get { return BookPackageInformation().Weight; } }
    }


    public class BookAsReadable : IReadable
    { 
        IBook _Book;

        public BookAsReadable(IBook book)
        { _Book = book; }

        public byte[] GetFrontCover()
        { throw new NotImplementedException(); }

        public byte[] GetRearCover()
        { throw new NotImplementedException(); }

        public byte[] GetIcon()
        { throw new NotImplementedException(); }

        public System.IO.Stream GetEpubContent()
        { throw new NotImplementedException(); }
    }


    class Program
    {
        static void Main(string[] args)
        {
            var adapterComponentList =
                    new List<Delegate> 
                    { 
                        //object supporting ITitle can be made to support IHeadline
                        Adapter.FromFunc((ITitle t) => new IHeadline(() => t.Title)),
 
                        //object supporting IHeadline can be made to support ITitle
                        Adapter.FromFunc((IHeadline h) => new ITitle(() => h.Headline)),


                        Adapter.FromFunc((IProperty<string> ip) => new ITitle(() => "Interface")),
                        Adapter.FromFunc((PropertyImp2 pi) => new IHeadline(() => "Class")),

                        //object supporting boyh ITitle and IHeadline can be made to support Erm.
                        Adapter.FromDelegate(new Func<ITitle,IHeadline,Erm>((t, h) => new Erm { _Headline = h, _Title = t }))
                    }
            ;

            var adapters = TvdP.Adapters.CreateComposed(adapterComponentList);

            Console.WriteLine("Can adapt Dub to IPorperty<object>?: {0}", adapters.Cast<IProperty<object>>(new Dub()) == null ? "No" : "Yes");

            adapterComponentList.Add(Adapter.FromFunc((Dub d) => new PropertyImp()));
            ((IResetable)adapters).Reset();
            
            Console.WriteLine("Can adapt Dub to IPorperty<object>?: {0}", adapters.Cast<IProperty<object>>(new Dub()) == null ? "No" : "Yes");

            Console.WriteLine(adapters.Cast<IProperty<object>>(new Dub()).ToString());

            //Console.WriteLine(compositor.Cast<ITitle>(new object()).Title);
            Console.WriteLine(adapters.Cast<ITitle>(new PropertyImp2()).Title);
            Console.WriteLine(adapters.Cast<ITitle>(new PropertyImp3()).Title);

            //Console.WriteLine(compositor.Cast<IHeadline>(new object()).Headline);
            Console.WriteLine(adapters.Cast<IHeadline>(new PropertyImp2()).Headline);
            Console.WriteLine(adapters.Cast<IHeadline>(new PropertyImp3()).Headline);

            Console.WriteLine(adapters.Cast<Erm>(new PropertyImp2()).Write());
            Console.WriteLine(adapters.Cast<Erm>(new PropertyImp3()).Write());

            Console.WriteLine(adapters.CanCast<PropertyImp2, Erm>());
            Console.WriteLine(adapters.CanCast<PropertyImp2, Program>());

            var behaviors =
                Behaviors.CreateComposed(
                    new[]
                    {
                        Behavior.FromDelegate(
                            new Action<IProperty<string>,object>(
                                (p, ctx) => 
                                { 
                                    var npc = p as INotifyPropertyChanged;
                                    var container = ctx as IUnityContainer;

                                    if (container != null && npc != null) 
                                        container.Resolve<LPGBehavior>().Attach(p, npc); 
                                }
                            )
                        )
                    },
                    adapters
                )
            ;

            using (var container = new UnityContainer())
            {
                container.AddExtension(new BehaviorsContainerExtension(behaviors));

                container.RegisterInstance<string>("prefix", "Singular:");

                var pimp3 = container.Resolve<PropertyImp3>();

                pimp3.Value = "Salty";
                pimp3.Value = "Chocolate";

                using (var childC = container.CreateChildContainer())
                {
                    childC.RegisterInstance<string>("prefix", "Plural:");

                    pimp3 = childC.Resolve<PropertyImp3>();

                    pimp3.Value = "Salties";
                    pimp3.Value = "Chocolates";

                    pimp3 = new PropertyImp3();

                    childC.BuildUp(pimp3);

                    pimp3.Value = "Bitters";                
                }

                container.RegisterInstance<IPackageInformationService>(new PackageInformationService());

                adapters = TvdP.Adapters.CreateComposed(
                    new []
                    {
                        Adapter.FromFunc((IBook b) => new BookAsPackagable(b, container.Resolve<IPackageInformationService>())),
                        Adapter.FromFunc((IBook b) => new BookAsReadable(b))

                    });

                var comparer = adapters.GetEqualityComparer();

                var book = new Book("NL099847739.4343");

                var bookForReader = adapters.Cast<IReadable>(book);
                var bookForPackaging = adapters.Cast<IPackagable>(book);

                Console.WriteLine("BookForReading == bookForPackaging? " + comparer.Equals(bookForReader, bookForPackaging));
            }


            Console.ReadKey();
        }
    }
}
